package com.one.blocks.mvc.excel;


import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.read.listener.ReadListener;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.one.blocks.common.request.PageRequest;
import com.one.blocks.common.response.ResponseHelper;
import com.one.blocks.common.response.WebResponse;
import com.one.blocks.mvc.controller.BaseController;
import com.one.blocks.mvc.convert.BaseConvert;
import com.one.blocks.mvc.domain.Id;
import com.one.blocks.mvc.enums.BatchStrategyEnum;
import com.one.blocks.mvc.manager.BaseManager;
import com.one.blocks.mvc.manager.BatchManager;
import org.springframework.http.HttpHeaders;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static com.one.blocks.common.constant.CommonConstant.DATE_SUP_SHORT_PATTERN_HH_MM;

/**
 * @author linfuxin Created on 2022-02-14 11:22:47
 */
public abstract class BaseExcelController<Manager extends BatchManager<T> & BaseManager<T> & ExcelManager<T>,
        Convert extends BaseConvert<T, V, Q> & ExcelConvert<T, E>, T extends Id<? extends Serializable>, V extends Id<? extends Serializable>, Q, E>
        extends BaseController<Manager, Convert, T, V, Q> {

    public static final String EXCEL_CONTENT_TYPE = "application/vnd.ms-excel";

    public BaseExcelController(Manager manager, Convert convert) {
        super(manager, convert);
    }

    public void generalDownload(Q query, PageRequest pageRequest, HttpServletResponse response) throws IOException {
        try (Page<E> page = PageHelper.startPage(pageRequest.getPageNum(), pageRequest.getPageSize(), false)) {
            List<T> data = page.doSelectPage(() -> manager.find(convert.queryToEntity(query)));
            writeExcel(convert.entityToExcelList(data), response);
        }
    }

    public WebResponse<UploadResult> generalUpload(MultipartFile file, UploadParam uploadParam) throws IOException {
        UploadResult uploadResult = new UploadResult();
        EasyExcel.read(file.getInputStream(), getExportClass(), buildReadListener(uploadParam, uploadResult)).sheet().doRead();
        return ResponseHelper.success(uploadResult);
    }

    public WebResponse<List<ExcelHeader>> generalGetExcelHeader() {
        Class<E> exportClass = getExportClass();

        Field[] fields = exportClass.getDeclaredFields();
        List<ExcelHeader> properties = new ArrayList<>(fields.length);
        for (Field field : fields) {
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                String[] excelHeaders = annotation.value();
                ExcelHeader data = new ExcelHeader();
                data.setName(field.getName());
                data.setLabel(excelHeaders.length == 1 ? excelHeaders[0] : excelHeaders[excelHeaders.length - 1]);
                properties.add(data);
            }
        }
        return ResponseHelper.success(properties);
    }

    protected ReadListener<E> buildReadListener(UploadParam uploadParam, UploadResult uploadResult) {
        BatchStrategyEnum batchStrategy = BatchStrategyEnum.getInstance(uploadParam.getExceptionStrategy());
        if (batchStrategy == BatchStrategyEnum.ROLLBACK) {
            return new RollbackOfErrorListener<>(manager, convert, uploadParam, uploadResult, getEntityClass(), getExportClass());
        } else if (batchStrategy == BatchStrategyEnum.IGNORE) {
            return new SkipOfErrorListener<>(manager, convert, uploadParam, uploadResult, getEntityClass(), getExportClass());
        } else if (batchStrategy == BatchStrategyEnum.BREAK_OFF) {
            return new BreakOfErrorListener<>(manager, convert, uploadParam, uploadResult, getEntityClass(), getExportClass());
        }
        throw new IllegalArgumentException();
    }

    @SuppressWarnings("unchecked")
    protected Class<E> getExportClass() {
        Type[] types = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
        return (Class<E>) types[types.length - 1];
    }

    @SuppressWarnings("unchecked")
    protected Class<T> getEntityClass() {
        Type[] types = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
        return (Class<T>) types[2];
    }

    protected void writeExcel(List<E> excelDataList, HttpServletResponse response) throws IOException {
        response.setContentType(EXCEL_CONTENT_TYPE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());

        Class<E> clazz = getExportClass();
        ExportName exportName = clazz.getAnnotation(ExportName.class);
        String fileName = exportName.value();
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
        fileName = fileName + LocalDateTime.now().format(DATE_SUP_SHORT_PATTERN_HH_MM);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), getExportClass())
                .sheet(exportName.sheet())
                .doWrite(excelDataList);
    }
}